package base.service.impl;

import base.entity.info.DirectoryInfo;
import base.entity.info.FileInfo;
import base.entity.info.Info;
import base.entity.regitem.DirRegItem;
import base.entity.regitem.RegItem;
import base.entity.serach.SearchResult;
import base.factory.impl.RegItemFactory;
import base.list.FatSingleton;
import base.list.SrcDirRegItemsSingleton;
import base.service.DirectoriesServer;
import base.util.RegItemUtils;
import base.view.DirectoriesView;
import base.view.View;
import test.FormatDisk;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

public class DirectoriesServerImpl implements DirectoriesServer {
    @Override
    public void md(String path) throws IOException {
        // 找到建立目录的位置
        if (path.indexOf("/") == -1){
            // 不存在分割符 ， 在跟目录创建名字为path的目录
            // 遍历跟目录，查看是否有同名的
            boolean hasSameName = false;
            for (int i = 0; i < SrcDirRegItemsSingleton.INSTANCE.size();i++){
                RegItem r = SrcDirRegItemsSingleton.INSTANCE.get(i);
                if(r == null || r.getName().equals(NULLREGITEM))continue;
                if(r.getName().equals(path)){
                    hasSameName = true;
                    break;
                }
            }
            if(hasSameName)throw new IOException("不能创建相同的目录");
            // 找到空闲的登记项
            boolean isOverflow = true;
            for (int i = 0; i<SrcDirRegItemsSingleton.INSTANCE.size();i++){
                RegItem r = SrcDirRegItemsSingleton.INSTANCE.get(i);
                if(r == null){
                    isOverflow = false;
                    // 遍历FAT表，找到空闲的磁盘
                    int j = 3;
                    while (j<FatSingleton.INSTANCE.size()){
                        if(FatSingleton.INSTANCE.get(j) == 0){
                            break;
                        }
                        j++;
                    }
                    if(j>=FatSingleton.INSTANCE.size())throw new IOException("磁盘空间已满");
                    FatSingleton.INSTANCE.set(j,-1); // 设置该磁盘结束，结束的
                    FatSingleton.INSTANCE.writeIntoFile(); // 保存
//                    System.out.println("分配的fat："+ j+"  value:"+FatSingleton.INSTANCE.get(j)); // 调试
                    List<RegItem> inList = new ArrayList<RegItem>();
                    // 创建8个空目录
                    RegItem emptyRegItem = null;
                    for(int k = 0; k< MAX_REGITEM;k++){
                         emptyRegItem = RegItemFactory.getInstance().newDirInstance("$  ",(byte)0,(byte)0); // 建立一个空目录
                        inList.add(emptyRegItem);
                    }
                    writeABlock(j,inList); // 将空目录写进dist
                    r = RegItemFactory.getInstance().newDirInstance(path,(byte)View.NOT_FILE,(byte)j);
                    SrcDirRegItemsSingleton.INSTANCE.set(i,r);
                    SrcDirRegItemsSingleton.INSTANCE.writeIntoFile(); // 将登记项写到跟目录
                    break;
                }
            }
            if (isOverflow) throw new IOException("根目录已达到最大容量");
        }
        else {
            // 不在跟目录下
            int blockNum = 2; // 一开始在根目录下
            String []steps = path.split("/");
            for (int i = 0; i< steps.length-1;i++){
                List<RegItem> regItems = readABlock(blockNum); // 读取该目录下的登记
                boolean hasMatch = false;
                for(RegItem regItem : regItems){
                    if (regItem.getName().equals(steps[i]) && (regItem.getProperty() & View.NOT_FILE) > 0){ // 名字匹配的目录
                        hasMatch = true;
                        blockNum = regItem.getBeginFatNum();
                        break;
                    }
                }
                if (!hasMatch){
                    throw new IOException("匹配不到"+steps[i]);
                }
            }
            // 现在blockNum指的是创建的目录的父目录登记项所在的盘块
            List<RegItem> parrentRegItems = readABlock(blockNum);
            if (hasRepeaet(parrentRegItems,steps[steps.length-1])){
                // 如果出现了重名
                throw new IOException(steps[steps.length-1]+"已存在");
            }
            boolean overflow = true; // 假设块内已满
            for (int i  = 0;i<MAX_REGITEM;i++){
                if (parrentRegItems.get(i).getName().indexOf("$") == 0){ // 找到第一个空的登记项
                    overflow =  false;
                    // 遍历FAT表，找到空闲的磁盘
                    int j = 3;
                    while (j<FatSingleton.INSTANCE.size()){
                        if(FatSingleton.INSTANCE.get(j) == 0){
                            break;
                        }
                        j++;
                    }
                    if(j>=FatSingleton.INSTANCE.size())throw new IOException("磁盘空间已满");
                    FatSingleton.INSTANCE.set(j,-1); // 设置该磁盘结束，结束的
                    FatSingleton.INSTANCE.writeIntoFile(); // 保存
                    List<RegItem> inList = new ArrayList<RegItem>();
                    // 创建8个空目录
                    RegItem emptyRegItem = null;
                    for(int k = 0; k< MAX_REGITEM;k++){
                        emptyRegItem = RegItemFactory.getInstance().newDirInstance("$  ",(byte)0,(byte)0); // 建立一个空目录
                        inList.add(emptyRegItem);
                    }
                    writeABlock(j,inList); // 将空目录写进dist
                    RegItem newr = RegItemFactory.getInstance().newDirInstance(steps[steps.length-1],(byte)View.NOT_FILE,(byte)j);
//                    SrcDirRegItemsSingleton.INSTANCE.set(i,r);
//                    SrcDirRegItemsSingleton.INSTANCE.writeIntoFile(); // 将登记项写到跟目录
                    parrentRegItems.set(i,newr);
                    writeABlock(blockNum,parrentRegItems);// 将修改好的父目录写入
                    writeABlock(j,inList); // 将目录写入
                    break;
                }
            }
            if (overflow)throw new IOException(path.substring(0,path.indexOf(steps[steps.length-1]))+"登记项已达最大数");
        }
    }

    @Override
    public DirectoriesView dir(String path) throws IOException {
        if (path.equals("")){
            // 跟目录
            return getRootDir();
        }
        String[] steps = path.split("/");
        int blockNum = 2; // 从跟目录开始
        StringBuilder sb = new StringBuilder(""); // 路径
        RegItem baseItem = null;
        for(int i = 0; i < steps.length;i++){
            // 目录名字小于3，补全
            while (steps[i].length() < 3) {
                steps[i] = steps[i] + " ";
            }
            boolean hasMatch = false;
            List<RegItem> items = readABlock(blockNum); // 读取
            for (RegItem item : items){
                if (item.getName().equals(steps[i]) && (item.getProperty() & View.NOT_FILE) > 0){ // 名称对应的目录
                    hasMatch = true;
                    blockNum = item.getBeginFatNum();
                    baseItem = item;
                    sb.append(item.getName()).append('/');
                    break;
                }
            }
            if (!hasMatch){
                throw new IOException(sb.toString()+"匹配不到："+steps[i]);
            }
        }
//        sb.append(steps[steps.length-1]);
//        System.out.println(sb.toString());
//        System.out.println("登记项在第"+blockNum+"块磁盘");
        List<RegItem> regItems  = readABlock(blockNum); // 读取该目录的所有登记项
//        System.out.println("baseItem:" + baseItem.getName());
        DirectoriesView view = new DirectoriesView(baseItem,sb.toString(),regItems);
//        for (RegItem item : regItems)System.out.println(item.getName() + "---->" + item.getBeginFatNum());

        return  view;
    }

    @Override
    public boolean rd(String path) throws IOException {
        if (path.equals(""))throw new IOException("不能删除跟目录");
        String []steps = path.split("/");
        if (steps.length<1)throw new IOException("不能删除跟目录");
        int preBlockNum = 2; // 上一个
        int blockNum = 2; // 从跟目录开始
        StringBuilder sb = new StringBuilder(""); // 路径
        RegItem baseItem = null;
        List<RegItem> items = null;
        for(int i = 0; i < steps.length;i++){
            boolean hasMatch = false;
            items = readABlock(blockNum); // 读取
            for (RegItem item : items){
                if (item.getName().equals(steps[i]) && (item.getProperty() & View.NOT_FILE) > 0){ // 名称对应的目录
                    hasMatch = true;
                    preBlockNum = blockNum;
                    blockNum = item.getBeginFatNum();
                    baseItem = item;
                    sb.append(item.getName()).append('/');
                    break;
                }
            }
            if (!hasMatch){
                throw new IOException(sb.toString()+"匹配不到："+steps[i]);
            }
        }
        List<RegItem> childItems = readABlock(baseItem.getBeginFatNum());
        if (isempty(childItems,"$  ")){
            FatSingleton.INSTANCE.set(baseItem.getBeginFatNum(),0); // 将目录项回收
            FatSingleton.INSTANCE.writeIntoFile(); // 保存
            baseItem.setName("$  ");
            baseItem.setBeginFatNum((byte) 0);
            writeABlock(preBlockNum, items);
            if (path.indexOf("/") == -1){
                // 删除跟目录下的文件
                for (int i = 0;i<SrcDirRegItemsSingleton.INSTANCE.size();i++){
                    RegItem regItem = SrcDirRegItemsSingleton.INSTANCE.get(i);
                    if (regItem!=null && regItem.getName().equals(path)){
                        regItem.setName(NULLREGITEM);
                        regItem.setBeginFatNum((byte)0);
                    }
                }
                SrcDirRegItemsSingleton.INSTANCE.writeIntoFile(); // 写入
            }
        }
        else {
            throw new IOException(path+"不是空目录");
        }


        return true;
    }

    @Override
    public List<RegItem> readABlock(int blockNum) {
        List<RegItem> regItems = new ArrayList<RegItem>();
        RegItem regItem = null;
        for(int i = 0;i<MAX_REGITEM;i++){
            regItem = RegItemUtils.read(file,64*blockNum + i*8);
            if (regItem == null){
                regItem = RegItemFactory.getInstance().newDirInstance("$  ",(byte)0,(byte)0); // 建立一个空目录
            }
            regItems.add(regItem);
        }
        return regItems;
    }

    @Override
    public void writeABlock(int blockNum, List<RegItem> dirRegItems) throws IOException {
        if (dirRegItems.size() > MAX_REGITEM)throw new IOException("写入块的登记项超过限制数");
        for (int i = 0;i < dirRegItems.size();i++){
            RegItemUtils.write(file, dirRegItems.get(i),64*blockNum + 8*i);
        }
    }

    @Override
    public boolean hasRepeaet(List<RegItem> regItems, String name){
        boolean rst = false;
        for(RegItem regItem : regItems){
            if (regItem.getName().equals(name)){
                rst = true;
            }
        }
        return rst;
    }

    @Override
    public boolean isempty(List<RegItem> regItems, String name) {
        boolean rst = true;
        for (RegItem regItem : regItems){
            if (!regItem.getName().equals(name)){
                rst = false;
                break;
            }
        }
        return rst;
    }

    @Override
    public List<SearchResult> serachRegItem(String index, int blocknum) {
        /**
         * 内部类，定义一个结构体，用于排队
         */
        class Queuer {
            Queuer(int blocknum,String basePath){
             this.blocknum = blocknum;
             this.basePath = basePath;
            }
          int blocknum;
          String basePath;
        }
        Queue<Queuer> queue = new LinkedList<Queuer>(); // 搜索队列
        List<SearchResult> searchResults = new ArrayList<SearchResult>(); // 返回的列表
        queue.add(new Queuer(blocknum,"")); // 根目录入队
        while (!queue.isEmpty()){ // 迭代，直到队列为空
            Queuer q = queue.poll();
            String basePath = q.basePath;
            int bNum = q.blocknum;
            List<RegItem> regItemList = readABlock(bNum); // 读取这个盘块的登记项
                for (RegItem regItem: regItemList){

                String name = regItem.getName();

                if (name.equals(NULLREGITEM))continue; // 空
                boolean isNotFile = (regItem.getProperty() & NOT_FILE) > 0; // 是否是文件
                String nextPath = basePath + "/" + name;
                if (isNotFile){
                    // 目录
                    int nextBlockNum = regItem.getBeginFatNum();
                    Queuer newQueuer = new Queuer(nextBlockNum,nextPath); // 新的排队元素
                    queue.add(newQueuer); // 入队
                    if (name.indexOf(index) >= 0){
                        // 如果包含搜索的索引
                        // 包装一个搜索结果
                        // 加入返回的结果队列
                        SearchResult result = new SearchResult(regItem,nextPath,false);
                        searchResults.add(result);
                    }
                } else {
                    // 文件
//                    System.out.println("jinlail");
                    if (name.indexOf(index) >= 0){
                        // 存在索引
                        // 包装一个搜索结果
                        // 加入返回的结果队列
                        SearchResult result = new SearchResult(regItem,nextPath,true);
                        searchResults.add(result);
                    }
                }
            }
//                System.out.println("一次队列迭代完成 " + basePath + "盘块号:"+ bNum );
        }
        return searchResults;
    }

    @Override
    public int getBlockNum(String path) throws IOException {
        String[] steps = path.split("/");
        int blockNum = 2;
        if (steps.length < 1)return 2;
        if (path.equals(""))return 2;
        for (int i = 0;i<steps.length;i++){
            String step = steps[i];
            List<RegItem> regItemList = readABlock(blockNum);
            boolean hasMatch = false;
            for (RegItem regItem:regItemList){
                if (regItem.getName().equals(NULLREGITEM))continue;
                if (regItem.getName().equals(step) && (regItem.getProperty()&View.NOT_FILE) > 0){ // 匹配到目录
                    hasMatch = true;
                    blockNum = regItem.getBeginFatNum();
                    break;
                }
            }
            if (!hasMatch)throw new IOException("匹配不到"+step);
        }
        return blockNum;
    }

    @Override
    public DirectoriesView getRootDir() {
        List<RegItem> childs = readABlock(2); // 直接读取
//        for (int i = 0; i< SrcDirRegItemsSingleton.INSTANCE.size();i++){
//            RegItem regItem = null;
//            regItem = SrcDirRegItemsSingleton.INSTANCE.get(i);
//            if (regItem == null){ // 如果为空，构造空登记项对象
//                regItem = RegItemFactory.getInstance().newDirInstance(NULLREGITEM,(byte)0,(byte)0);
//            }
//            childs.add(regItem);
//        }
        DirectoriesView view = new DirectoriesView(null,"",childs);
        return view;
    }

    public static void main(String[] args) throws IOException {
       FormatDisk formatDisk = new FormatDisk();
//       formatDisk.format();
       DirectoriesServer server = new DirectoriesServerImpl();
//        server.md("kdy");
//        server.rd("kdy");
//        for (int i = 0;i<SrcDirRegItemsSingleton.INSTANCE.size();i++){
//            RegItem regItem = SrcDirRegItemsSingleton.INSTANCE.get(i);
//            if (regItem != null)System.out.println(regItem.getName());
//        }
//        System.out.println("----------------------------");
////        server.md("bbb");
//        server.md("aaa/acc");
//        server.md("aaa/abc");
//        server.md("bbb/cac");

//        List<SearchResult> results = server.serachRegItem("a",2);
//        System.out.println("--------");
//        for (SearchResult result:results){
//            System.out.println(result);
//        }
//        System.out.println("--------");
////       String name = "aaa";
////       System.out.println(name +"长度为" + name.getBytes().length);
//       server.md(name);
//        server.md("aaa");
//        server.md("ccc");
//        server.md("aaa/jav");
//        server.md("aaa/c++");
//        server.md("aaa/c++/adc");
//        server.md("aaa/c++/adc/aaa");
//        server.md("aaa/c++/adc/aaa/aaa");
////        server.md("ddd");
//        server.md("eee");
//        server.md("fff");
//        server.md("ggg");
//        server.md("hhh");
//        DirectoriesView view = server.dir("aaa/acc");
//        System.out.println(server.getBlockNum("aaa/acc"));
//        System.out.println(view.getPath());
//        List<Info> infos = view.getChildDirInfo();
//        for (Info info : infos)System.out.println(info);

//        DirectoriesView view2 = server.dir("aaa");
//        List<SearchResult> results1 = view2.doSearch("a");
//        for (SearchResult result : results1)System.out.println(result);
////        server.rd("/");
////
//        DirectoriesView view2 = server.dir("aaa");
//        List<SearchResult> results1 = view2.doSearch("a");
//        for (SearchResult result : results1)System.out.println(result);
//        List<FileInfo> finfos = view2.serach2FileInfo(results1);
//        List<Info> dinfos = view2.serach2DirectoriesInfo(results1);
//        System.out.println("info:--------------------------------");
//        for (Info info : finfos)System.out.println((FileInfo)info);
//        for (Info info : dinfos)System.out.println((DirectoryInfo)info);
//        System.out.println("info:--------------------------------");
////////        server.rd("/");
//////
//        List<RegItem> regItems = server.readABlock(2);
//        for (int i = 0;i<MAX_REGITEM;i++){
//            System.out.println(i+ " :" + regItems.get(i).getBeginFatNum() +" " + regItems.get(i).getName());
//        }


        formatDisk.show();


    }

}
